package org.wheel.jpa.factory;

import java.io.Serializable;
import java.util.Optional;

import javax.persistence.EntityManager;

import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.jpa.provider.QueryExtractor;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.query.DefaultJpaQueryMethodFactory;
import org.springframework.data.jpa.repository.query.EscapeCharacter;
import org.springframework.data.jpa.repository.query.JpaQueryMethodFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.wheel.jpa.query.BaseJpaQueryLookupStrategy;

/**
 * @author klaus.wang
 */
public class BaseRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
		extends JpaRepositoryFactoryBean<R, T, I> {
	/**
	 * Creates a new {@link JpaRepositoryFactoryBean} for the given repository
	 * interface.
	 *
	 * @param repositoryInterface must not be {@literal null}.
	 */
	public BaseRepositoryFactoryBean(Class<? extends R> repositoryInterface) {
		super(repositoryInterface);
	}

	@Override
	protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) {
		return new BaseRepositoryFactory<T, I>(em);
	}

	private static class BaseRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {

		private final EntityManager entityManager;

		private final QueryExtractor extractor;

		private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT;

		private JpaQueryMethodFactory queryMethodFactory;

		public BaseRepositoryFactory(EntityManager entityManager) {
			super(entityManager);
			this.entityManager = entityManager;
			this.extractor = PersistenceProvider.fromEntityManager(entityManager);
			this.setEscapeCharacter(escapeCharacter);
			this.setQueryMethodFactory(queryMethodFactory = new DefaultJpaQueryMethodFactory(extractor));
		}

		@Override
		protected Optional<QueryLookupStrategy> getQueryLookupStrategy(QueryLookupStrategy.Key key,
				QueryMethodEvaluationContextProvider evaluationContextProvider) {
			return Optional.of(BaseJpaQueryLookupStrategy.create(entityManager, queryMethodFactory, key,
					evaluationContextProvider, escapeCharacter));
		}

	}

}